/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: plugin_manager.cpp
 *
 * Purpose: plugin manager implementation
 *
 * Developer:
 *   wen.gu , 2019-08-18
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "panda/core/plugin_manager.h"


#include <map>

#include "panda/core/plugin.h"    
#include "panda/core/shared_library.h"
#include "panda/core/errors.h"

#define LOG_TAG "plgm"
#include "panda/core/log.h"

namespace panda
{
namespace core
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

struct PluginHandle
{
    void* dlHd;
    std::string name;
};

typedef std::map<Plugin*, PluginHandle> PluginLibraryMap_t;

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
 class PluginManager::impl
{
public:
     PandaErrc load_l(const std::string& name);
     bool libIsLoaded(const std::string& name);

public:
     PluginLibraryMap_t mLibMap;
     std::mutex mLock;
};


 PandaErrc PluginManager::impl::load_l(const std::string& name)
 {
     if (libIsLoaded(name) == false)
     {
         SharedLibHandle dlHd = SharedLibOpen(name.c_str());

         if (dlHd != INVALID_LIB_HANDLE)
         {
             pluginEntryFunc_t pluginEntry =
                 (pluginEntryFunc_t)SharedLibSym(dlHd, PLUGIN_ENTRY_NAME);
             pluginHdrVerFunc_t verFunc =
                 (pluginHdrVerFunc_t)SharedLibSym(dlHd, PLUGIN_VER_FUNC_NAME);

             if (pluginEntry && verFunc)
             {
                 const std::string ver = verFunc();

                 if (ver == PLUGIN_HEADER_VER)
                 {/** todo, process the compatibility of master version */
                     Plugin* mp = pluginEntry();
                     
                     PluginHandle mph;
                     mph.dlHd = dlHd;
                     mph.name = name;
                     mLibMap[mp] = mph;
                     LOGD("load plugin(%s, ver: %s, author: %s) success "
                         "with plugin interface ver:%s\n",
                         mp->name().c_str(), mp->version().c_str(),
                         mp->author().c_str(), PLUGIN_HEADER_VER);
                     return PandaErrc::OK;
                 }
                 else
                 {
                     LOGE("version mismatch, host(%s), plugin(%s)\n",
                         PLUGIN_HEADER_VER, ver.c_str());
                     return PandaErrc::Undefined;
                 }
             }
             else
             {
                 LOGE("lib(%s) haven't entry func(%s, %s)\n",
                     name.c_str(), PLUGIN_ENTRY_NAME,
                     PLUGIN_VER_FUNC_NAME);

                 return PandaErrc::Undefined;
             }
         }
         else
         {
             LOGE("load lib(%s) failed(%s)\n", name.c_str(), SharedLibError().c_str());
         }
     }
     else
     {
         LOGE("lib(%s) already loaded\n", name.c_str());
     }

     return PandaErrc::InvalidStatus;
 }

 bool PluginManager::impl::libIsLoaded(const std::string& name)
 {
     bool ret = false;
     PluginLibraryMap_t::iterator it = mLibMap.begin();

     for (; it != mLibMap.end(); it++)
     {
         PluginHandle& mph = it->second;

         if (name == mph.name)
         {
             ret = true;
             break;
         }
     }

     return ret;
 }


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
 PluginManager::PluginManager()
     :mImpl(new impl)
 {
     /**todo, something */
 }

 PluginManager::~PluginManager()
 {
     unloadAll();
     /**todo, something */
 }

 //static
 PluginManager& PluginManager::getInstance()
 {
     static PluginManager mpm;
     return mpm;
 }


 PandaErrc PluginManager::load(const std::string& name)
 {
     //AutoLock al(mLock);
     return mImpl->load_l(name);
 }

 PandaErrc PluginManager::loadList(std::vector<std::string>& pluginList,
     bool ignoreError/* = true*/)
 {
     PandaErrc ret = PandaErrc::OK;
     uint32_t loadCnt = 0;
     //AutoLock al(mLock);

     for (uint32_t i = 0; i < pluginList.size(); i++)
     {
         ret = mImpl->load_l(pluginList[i]);

         if (bool(ret))
         {
             loadCnt++;
         }
         else if (ignoreError)
         {
             ret = PandaErrc::OK;
         }
         else
         {
             LOGE("load plugin(%s) failed(%d, %s)\n", pluginList[i].c_str(), ret, ErrorStr(ret));
             break;
         }
     }

     LOGD("loaded plugin(%d)\n", loadCnt);

     return ret;
 }


 PandaErrc PluginManager::unloadAll()
 {
     //AutoLock al(mLock);

     PluginLibraryMap_t::iterator it = mImpl->mLibMap.begin();

     for (; it != mImpl->mLibMap.end(); it++)
     {
         Plugin* mp = it->first;
         PluginHandle& mph = it->second;

         if (mp)
         {
             delete mp;
         }

         if (mph.dlHd)
         {
             SharedLibClose(mph.dlHd);
         }
     }

     mImpl->mLibMap.clear();

     return PandaErrc::OK;
 }

PandaErrc PluginManager::find(const ComponentCreateParam* param, Component** comp)
 {
    PandaErrc ret = PandaErrc::NotFound;
     //AutoLock al(mLock);
     PluginLibraryMap_t::iterator it = mImpl->mLibMap.begin();

     for (; it != mImpl->mLibMap.end(); it++)
     {
         Plugin* mp = it->first;
         if (mp)
         {
             LOGD("===>(type: %s, plugin: %s)\n",
                 param->type.c_str(), mp->name().c_str());

             ret = mp->createComponent(param, comp);

             //LOGD("===>ret(%d, %s)\n", ret.code, ret.desc);
             if (ret == PandaErrc::OK)
             {
                 LOGD("create component instance success, name: %s, version: %s\n",
                     (*comp)->name().c_str(), (*comp)->version().c_str());
                 break;
             }
         }
     }

     return ret;
 }


} /** namespace core */
} /** namespace panda */
